﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using GeoFramework;
using System.Threading;
using System.IO;
using System.Drawing;
using System.Net;

namespace MapControl
{
    internal abstract class CommonImageManager : IImageManager
    {
        private class ThreadArgs
        {
            public bool IsDownloadingTile { get; set; }
        }

        protected List<int> processedHashes = new List<int>();
        private static Object locker = new object();

        public abstract string CachePath { get; }
        protected abstract string GetUrl(Position position, int zoomLevel, MapType mapType);

        public CommonImageManager()
        {
            if (!Directory.Exists(CachePath))
                Directory.CreateDirectory(CachePath);
        }

        protected virtual void TryLoadFromCache(MapTile tile, string cachedImg)
        {
            ThreadPool.QueueUserWorkItem(delegate(object state)
            {
                Bitmap bmp;
                using (StreamReader reader = new StreamReader(cachedImg))
                {
                    try
                    {
                        bmp = new Bitmap(reader.BaseStream);
                    }
                    finally
                    {
                        reader.Close();
                    }
                }
                tile.TileImage = bmp;
                if (ImageSetted != null)
                    ImageSetted(this, EventArgs.Empty);
                lock (locker)
                {
                    if(processedHashes.Contains(tile.GetHashCode()))
                        processedHashes.Remove(tile.GetHashCode());
                }
            });
        }

        #region IImageManager Members

        public event EventHandler ImageSetted;

        public virtual void LoadImage(MapTile tile)
        {
            lock (locker)
            {
                if (processedHashes.Contains(tile.GetHashCode()))
                    return;
            }
            string cachedImg = Path.Combine(CachePath, string.Format("{3}_{0}_{1}_{2}.png", tile.ZoomLevel,
                tile.Position.Latitude.DecimalDegrees, tile.Position.Longitude.DecimalDegrees, (int)tile.MapType));
            lock (locker)
            {
                processedHashes.Add(tile.GetHashCode());
            }
            if (File.Exists(cachedImg) && new FileInfo(cachedImg).Length > 0)
            {
                TryLoadFromCache(tile, cachedImg);
                return;
            }

            ThreadArgs ta = new ThreadArgs() { IsDownloadingTile = false };

            Thread t = new Thread(new ThreadStart(delegate()
            {

                HttpWebRequest wreq;
                HttpWebResponse wresp;
                Stream mystream;


                Bitmap bmp = null;
                mystream = null;
                wresp = null;
                try
                {
                    wreq = (HttpWebRequest)WebRequest.Create(GetUrl(tile.Position, tile.ZoomLevel, tile.MapType));
                    wreq.AllowWriteStreamBuffering = true;

                    try
                    {
                        ta.IsDownloadingTile = true;
                        wresp = (HttpWebResponse)wreq.GetResponse();
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(ex);
                        return;
                    }
                    finally
                    {
                        ta.IsDownloadingTile = false;
                    }

                    if ((mystream = wresp.GetResponseStream()) != null)
                    {
                        bmp = new Bitmap(mystream);
                        using (StreamWriter writer = new StreamWriter(cachedImg))
                        {
                            try
                            {
                                bmp.Save(writer.BaseStream, System.Drawing.Imaging.ImageFormat.Png);
                            }
                            finally
                            {
                                writer.Close();
                            }
                        }
                    }
                }
                finally
                {
                    if (mystream != null)
                        mystream.Close();

                    if (wresp != null)
                        wresp.Close();
                }
                tile.TileImage = bmp;
                //lock (locker)
                //{
                    if (ImageSetted != null)
                        ImageSetted(this, EventArgs.Empty);
                    lock (locker)
                    {
                        if (processedHashes.Contains(tile.GetHashCode()))
                            processedHashes.Remove(tile.GetHashCode());
                    }
                //}
            }));

            tile.ImageDisposing += new EventHandler(delegate(object sender, EventArgs ea)
            {
                if (ta != null && t != null && ta.IsDownloadingTile)
                    t.Abort();
            });

            t.Name = string.Format("TileDownloader_{0}", tile.GetHashCode());
            t.Start();
        }

        #endregion
    }
}
